package wrappers

import (
	"context"
	"gitee.com/banyanhouse/distill-micro-grpc-hystrix/micro-gateway/Services"
	"github.com/afex/hystrix-go/hystrix"
	"github.com/micro/go-micro/v2/client"
	"github.com/micro/go-micro/v2/client/selector"
	"github.com/micro/go-micro/v2/metadata"
	"strconv"
)

type ProdsWrapper struct {
	client.Client
}

func NewProdsWrapper(c client.Client) client.Client {
	return &ProdsWrapper{c}
}

func (this *ProdsWrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {

	commandConfig := hystrix.CommandConfig{
		Timeout:                1000,
		MaxConcurrentRequests:  1,
		RequestVolumeThreshold: 2,    // 默认20，如果错误超过该次数，才开始计算错误百分比
		ErrorPercentThreshold:  50,   // 错误百分比，默认50，即50%
		SleepWindow:            5000, // 过多长时间，熔断器再次检测是否开启。单位毫秒（默认5秒）
	}
	serviceEndPointName := req.Service() + "." + req.Endpoint()
	commandMap[serviceEndPointName] = commandConfig

	// fetch metadata from context
	md, _ := metadata.FromContext(ctx)

	// define a filter closure to only select server with matching metadata configured
	//filter := func(services []*registry.Service) []*registry.Service {
	//	for _, service := range services {
	//		var nodes []*registry.Node
	//		for _, node := range service.Nodes {
	//			if node.Metadata["datacenter"] == md["datacenter"] {
	//				nodes = append(nodes, node)
	//			}
	//		}
	//		service.Nodes = nodes
	//	}
	//	return services
	//}

	// extend options with SelectionOption
	callOptions := append(opts, client.WithSelectOption(
		//selector.WithFilter(filter),
		selector.WithFilter(selector.FilterLabel("datacenter", md["datacenter"])),
		//selector.WithStrategy(selector.Random),
	))

	err := hystrix.Do(serviceEndPointName, func() error {
		return this.Client.Call(ctx, req, rsp, callOptions...)
	}, func(err error) error {
		defaultData(rsp) // defaultProds(rsp)
		return nil
	})
	return err
}

// 通用降级方法
func defaultData(rsp interface{}) {
	switch t := rsp.(type) {
	case *Services.ProdListResponse:
		defaultProds(rsp)
	case *Services.ProdDetailResponse:
		t.Data = newProd(10, "降级商品")
	}
}

// 商品列表降级方法
func defaultProds(rsp interface{}) {
	models := make([]*Services.ProdModel, 0)
	var i int32
	for i = 0; i < 3; i++ {
		models = append(models, newProd(20+i, "prodName"+strconv.Itoa(20+int(i))))
	}
	result := rsp.(*Services.ProdListResponse)
	result.Data = models
}

// 测试方法
func newProd(id int32, pname string) *Services.ProdModel {
	return &Services.ProdModel{
		ProdId:   id,
		ProdName: pname,
	}
}
